#include "lib.h"

#include "SicBoSQL.h"
#include "CrapConn.h"
#include "DiceGameIds.h"
#include "SicBoSharedDefs.h"
#include "odbc.h"
#include "opts.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     SicBoSQL::SicBoSQL(CrapsConnection *pConn, TrackSQL *pSQL):GameDBInterface(pConn, pSQL)
///
/// constructor for object that handles database communication for the sicbo game
///
/// @param  pConn   -   (in)    object that represents our connection to the player via the network
/// @param  pSQL    -   (in)    pointer to TrackSQL singleton that REALLY controls access to the DB
///
////////////////////////////////////////////////////////////////////////////////////////////////////
SicBoSQL::SicBoSQL(CrapsConnection *pConn, TrackSQL *pSQL):GameDBInterface(pConn, pSQL)
{
    memset(&m_options,0,sizeof(m_options));
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     SicBoSQL::trackHasSavedGame(DiceGame *& pGame, int32 *err)
///
/// Must implement pure virtual function from base class, but does nothing because sicbo has no
/// saved games.
////////////////////////////////////////////////////////////////////////////////////////////////////
bool8 SicBoSQL::trackHasSavedGame(DiceGame *& pGame, int32 *err)
{
    *err  = 0;
    pGame = NULL;

    return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     SicBoSQL::getTableLimits()
///
/// Simple accessor object holding game's betting limits 
///
////////////////////////////////////////////////////////////////////////////////////////////////////
SicBoOptions * SicBoSQL::getOptions()
{
    return &m_options;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     SicBoSQL::trackRoll()
///
/// This method resultins in saving all data for a single round of SicBo to the 
/// the database and returns the updated balance which reflects the results of the round.
///
/// @param ucDie1       - (in)  face value of die 1
/// @param ucDie2       - (in)  face value of die 2
/// @param ucDie3       - (in)  face value of die 3
/// @param pBetInfo     - (in)  array of all available bets, including ones that had no bet placed
/// @param nNumBets     - (in)  number of bets that the user placed
/// @param nTotalBet    - (in)  total value of all bets placed
/// @param nTotalPayout - (in)  the amount that the player won, not including original 
///                             bets lost or returned.
/// @param nBalAdjust   - (in)  Total amount won, including bets returned, but not bets lost
/// @param pnBalance    - (out) the player's balance after factoring in the result of this roll.
///
/// @return bool8   true if successful, false on failure.
////////////////////////////////////////////////////////////////////////////////////////////////////
bool8 SicBoSQL::trackRoll(uint8 ucDie1, uint8 ucDie2, uint8 ucDie3, SicBoBet *pBetInfo, 
                          int32 nNumBets, int32 nTotalBet, int32 nTotalPayout, 
                          int32 nBalAdjust, int32 *pnBalance)
{
    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();
    int32 nResult = TrackSQL::ExecFailed;

    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();

        nResult = roll(info, ucDie1, ucDie2, ucDie3, pBetInfo, nNumBets, 
                       nTotalBet, nTotalPayout, nBalAdjust, pnBalance);

        m_pTrackSQL->unlock();
    }

    return (bool8)(nResult == TrackSQL::NoError);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     SicBoSQL::roll()
///
/// This is the main method of this class.  Saves all data for a single round of SicBo to the 
/// the database and returns the updated balance which reflects the results of the round.
///
/// @param ucDie1       - (in)  face value of die 1
/// @param ucDie2       - (in)  face value of die 2
/// @param ucDie3       - (in)  face value of die 3
/// @param pBetInfo     - (in)  array of all available bets, including ones that had no bet placed
/// @param nNumBets     - (in)  number of bets that the user placed
/// @param nTotalBet    - (in)  total value of all bets placed
/// @param nTotalPayout - (in)  the amount that the player won, not including original 
///                             bets lost or returned.
/// @param nBalAdjust   - (in)  Total amount won, including bets returned, but not bets lost
/// @param pnBalance    - (out) the player's balance after factoring in the result of this roll.
///
/// @return bool8   true if successful, false on failure.
////////////////////////////////////////////////////////////////////////////////////////////////////
int32 SicBoSQL::roll(CrapsConnection::Info *pGameInfo, uint8 ucDie1, uint8 ucDie2, uint8 ucDie3, 
                     SicBoBet *pBetInfo, int32 nNumBets, int32 nTotalBet, 
                     int32 nTotalPayout, int32 nBalAdjust, int32 *pnBalance)
{
    ODBCConnection *pConn;
    ODBCQuery *pQuery;
    bool8 bOK = false;

    pConn = m_pTrackSQL->getConn();
    if (!pConn)
    {
        return TrackSQL::NoConnection;
    }    

    pQuery = pConn->query();
    if(pQuery)
    {
        pQuery->define("exec pub_CreateGameSicBo ");
        pQuery->addParam(ServerOpts::casinoId);
        pQuery->addParam(pGameInfo->pid);
        pQuery->addParam((bool8)(pGameInfo->forMoney != 0));
        pQuery->addParam(pGameInfo->sessionId, ODBCQuery::NOQUOTES);
        pQuery->addParam(pGameInfo->forMoney ? m_options.nCompFactor : 0);
        pQuery->addParam(ucDie1);
        pQuery->addParam(ucDie2);
        pQuery->addParam(ucDie3);
        pQuery->addParam(nNumBets);
        pQuery->addParam(nTotalBet, ODBCQuery::MONEY);
        pQuery->addParam(nTotalPayout, ODBCQuery::MONEY);
        pQuery->addParam(nBalAdjust, ODBCQuery::MONEY);

        CrapsGameID gid;

        if (pQuery->exec())
        {
            while (pQuery->getRow())
            {
                bOK = pQuery->getData(1, gid, CRAPS_GAMEID_SIZE) &&
                      pQuery->getData(2, pnBalance, ODBCQuery::MONEY);

                if(strcmp(gid,"-1")==0)
                {   
                    // gid will be -1 if something went wrong in the stored procedure.
                    bOK=false;
                }
            }
        }
        
        if (bOK && pQuery->reset())
        {
            // the main record has been created, so now all secondary records, one for each bet that was placed.
            String strBetBuf;

            for (int32 nI=0; nI < SicBoBet::MAX_BET_TYPE; ++nI)
            {
                if (pBetInfo[nI].nBetAmt>0)
                {
                    pQuery->define("exec pub_SicBoAddBet ");
                    pQuery->addParam(gid, ODBCQuery::NOQUOTES);
                    pQuery->addParam((uint8) nI);
                    pQuery->addParam(pBetInfo[nI].nBetAmt, ODBCQuery::MONEY);
                    pQuery->append(" \n");

                    // add this call to our string...we'll pass them all at once to the database,
                    // thus saving on round trips. 
                    strBetBuf += pQuery->getQuery();
                }
            }

            bOK = false;

            // execute the string containing all of the bet adds.
            if (pQuery->exec(strBetBuf))
            {
                bOK = true;

                while (pQuery->getRow()); /* ignore */
            }
        }

        delete pQuery;
    }
    delete pConn;


    return bOK ? TrackSQL::NoError : TrackSQL::ExecFailed;
}





//////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn    SicBoSQL::trackLoadOptions()
///
/// Get the configured min/max betting limits, compFactor, and paytable for SicBo
/// 
/// @return bool8           true on success, false otherwise.
///
//////////////////////////////////////////////////////////////////////////////////////////////////////
bool8 SicBoSQL::trackLoadOptions()
{
    int32 result = TrackSQL::ExecFailed;

    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();

    bool8 bOK = false;

    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();

        result = m_pTrackSQL->getPlayerLimits(info->pid, SICBO_GAME_ID, 0, &m_options.nMinLimit, &m_options.nMaxLimit, &m_options.nCompFactor);

        if(result == TrackSQL::NoError)
        {
            ODBCConnection *pConn;
            ODBCQuery *pQuery;

            pConn = m_pTrackSQL->getConn();
            if (!pConn)
            {
                return false;
            }    

            pQuery = pConn->query();
            if(pQuery)
            {
                pQuery->define("exec pub_SicBoGetOptions ");

                if (pQuery->exec())
                {
                    while (pQuery->getRow())
                    {
                        bOK = pQuery->getData(1, &m_options.ucPayTable);
                    }
                }
                delete pQuery;
            }
            delete pConn;

        }

        m_pTrackSQL->unlock();
    }

    return bOK;
}


#ifdef DEBUG_KNOWNROLLS

// Testing logic.  Loads in spins from debug table.
bool8 SicBoSQL::bLoaded = false;
uint8 SicBoSQL::aucDice[255][3];
int32 SicBoSQL::nMaxRoll = 255;
int32 SicBoSQL::nCurrRoll = 0;


void SicBoSQL::getDebugRoll(uint8 *pucDie1, uint8 *pucDie2, uint8 *pucDie3)
{
    if(bLoaded==false)
    {
        bLoaded = true;

        for(int nI=0;nI<255;++nI)
        {
            aucDice[nI][0] = aucDice[nI][1] = aucDice[nI][2] = 1;
        }

        m_pTrackSQL->lock();

        ODBCConnection *pConn;
        ODBCQuery *pQuery;

        pConn = m_pTrackSQL->getConn();
        if (!pConn)
        {
            m_pTrackSQL->unlock();
            return;
        }    

        pQuery = pConn->query();
        if(pQuery)
        {
            pQuery->define("select Die1, Die2, Die3 from SicBoDebug order by rollnum");

            if (pQuery->exec())
            {
                nMaxRoll = 0;
                while (pQuery->getRow() && nMaxRoll < 255)
                {
                    pQuery->getData(1, &aucDice[nMaxRoll][0]);
                    pQuery->getData(2, &aucDice[nMaxRoll][1]);
                    pQuery->getData(3, &aucDice[nMaxRoll][2]);

                    ++nMaxRoll;
                }
            }
            delete pQuery;
        }

        delete pConn;

        m_pTrackSQL->unlock();
    }

    if(nCurrRoll>=nMaxRoll)
    {
        nCurrRoll = 0;
    }

    *pucDie1 = aucDice[nCurrRoll][0];
    *pucDie2 = aucDice[nCurrRoll][1];
    *pucDie3 = aucDice[nCurrRoll][2];    

    ++nCurrRoll;
}

#endif